{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Deep MNIST classifiers\n",
    "\n",
    "## Reproducing the results from **Programmable Bootstrapping Enables Efficient Homomorphic Inference of Deep Neural Networks**\n",
    "\n",
    "This notebook replicates experiments from the paper [_Programmable Bootstrapping Enables Efficient Homomorphic Inference of Deep Neural Networks_](https://whitepaper.zama.ai/), published in 2021. This previous work did not use the Concrete compiler.\n",
    "This notebook reproduces the deep neural network architectures NN-20 and NN-50 from the whitepaper and shows how they are [quantized](https://docs.zama.ai/concrete-ml/explanations/quantization) using Post Training Quantization (PTQ) and compiled with Concrete to work on encrypted data.\n",
    "\n",
    "We compare the original paper's findings with the results from the latest version of [Concrete ML](https://pypi.org/project/concrete-ml/). \n",
    "\n",
    "The results shown at the end of this notebook show a large speed-up with respect to the results in the whitepaper."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import random\n",
    "import time\n",
    "import warnings\n",
    "\n",
    "import numpy as np\n",
    "import torch\n",
    "from torchvision import datasets, transforms\n",
    "from utils_experiments import MEAN, STD, format_results_df, plot_dataset, torch_inference\n",
    "\n",
    "from concrete.ml.torch.compile import compile_torch_model\n",
    "\n",
    "warnings.filterwarnings(\"ignore\", category=UserWarning)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Architecture\n",
    "\n",
    "All networks begin with a convolutional layer configured with `in_channel=1, out_channels=1, kernel_size=3, stride=1, padding_mode='replicate'`. \n",
    "\n",
    "This is followed by 20 linear layers of 92 neurones with ReLU activation for NN-20, and 50 layers for NN-50."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "DEVICE = \"cpu\"\n",
    "\n",
    "# Input size, 28x28 pixels, a standard size for MNIST images\n",
    "INPUT_IMG_SIZE = 28\n",
    "\n",
    "# Batch size\n",
    "BATCH_SIZE = 64\n",
    "\n",
    "# Seed to ensure reproducibility\n",
    "SEED = 42\n",
    "\n",
    "# Whether the experiments are run on PC or other machines, like HP7C on AWS\n",
    "MACHINE = f\"{os.cpu_count()}-core machine\"\n",
    "\n",
    "# The timing and the accuracy recorded in the article\n",
    "if os.cpu_count() > 48:\n",
    "    PAPER_NOTES = {20: [21.17, 0.971], 50: [43.91, 0.947]}\n",
    "else:\n",
    "    PAPER_NOTES = {20: [115.52, 0.971], 50: [233.55, 0.947]}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## FP32 MNIST Neural Nerwork "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "FEATURES_MAPS = [\n",
    "    # Convolution layer, with:\n",
    "    # in_channel=1, out_channels=1, kernel_size=3, stride=1, padding_mode='replicate'\n",
    "    (\"C\", 1, 1, 3, 1, \"replicate\"),\n",
    "    (\"R\",),\n",
    "    (\"B\", 1, 30),  # 2d batch-norm for 1 channel\n",
    "]\n",
    "\n",
    "\n",
    "# The article presents 3 neural network depths. In this notebook, we focus NN-20 and NN-50\n",
    "# architectures. The parameter `nb_layers`: controls the depth of the NN.\n",
    "def LINEAR_LAYERS(nb_layers: int, output_size: int):\n",
    "    return (  # noqa: W503\n",
    "        [\n",
    "            (\"L\", INPUT_IMG_SIZE * INPUT_IMG_SIZE, 92),\n",
    "            (\"R\",),\n",
    "            (\"B\", 92),  # 1d batch norm\n",
    "        ]  # noqa: W503\n",
    "        + [  # noqa: W503\n",
    "            (\"L\", 92, 92),\n",
    "            (\"R\",),\n",
    "            (\"B\", 92),  # 1d batch norm\n",
    "        ]\n",
    "        * (nb_layers - 3)  # noqa: W503\n",
    "        + [(\"L\", 92, output_size)]  # noqa: W503\n",
    "    )\n",
    "\n",
    "\n",
    "class Fp32MNIST(torch.nn.Module):\n",
    "    \"\"\"MNIST Torch model.\"\"\"\n",
    "\n",
    "    def __init__(self, nb_layers: int, output_size: int = 10):\n",
    "        \"\"\"MNIST Torch model.\n",
    "\n",
    "        Args:\n",
    "            nb_layers (int): Number of layers.\n",
    "            output_size (int): Number of classes.\n",
    "        \"\"\"\n",
    "        super().__init__()\n",
    "\n",
    "        self.nb_layers = nb_layers\n",
    "        self.output_size = output_size\n",
    "\n",
    "        def make_layers(t):\n",
    "            if t[0] == \"C\":\n",
    "                # Workaround: stride=1, padding_mode='replicate' is replaced by\n",
    "                # transforms.Pad(1, padding_mode=\"edge\")\n",
    "                return torch.nn.Conv2d(\n",
    "                    in_channels=t[1],\n",
    "                    out_channels=t[2],\n",
    "                    kernel_size=t[3],\n",
    "                )\n",
    "            if t[0] == \"L\":\n",
    "                return torch.nn.Linear(in_features=t[1], out_features=t[2])\n",
    "            if t[0] == \"R\":\n",
    "                return torch.nn.ReLU()\n",
    "            if t[0] == \"B\":\n",
    "                if len(t) == 2:\n",
    "                    return torch.nn.BatchNorm1d(t[1])\n",
    "                if len(t) == 3:\n",
    "                    return torch.nn.BatchNorm2d(t[1])\n",
    "\n",
    "            raise NameError(f\"'{t}' not defined\")\n",
    "\n",
    "        self.features_maps = torch.nn.Sequential(*[make_layers(t) for t in FEATURES_MAPS])\n",
    "        self.linears = torch.nn.Sequential(\n",
    "            *[make_layers(t) for t in LINEAR_LAYERS(self.nb_layers, self.output_size)]\n",
    "        )\n",
    "\n",
    "    def forward(self, x):\n",
    "        x = self.features_maps(x)\n",
    "        x = torch.nn.Flatten()(x)\n",
    "        x = self.linears(x)\n",
    "        return x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Load and pre-process the MNIST data-set\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz\n",
      "Failed to download (trying next):\n",
      "HTTP Error 403: Forbidden\n",
      "\n",
      "Downloading https://ossci-datasets.s3.amazonaws.com/mnist/train-images-idx3-ubyte.gz\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Downloading https://ossci-datasets.s3.amazonaws.com/mnist/train-images-idx3-ubyte.gz to ./data/MNIST/raw/train-images-idx3-ubyte.gz\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "db11663fea1e45f286356e7dbd1c9b15",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "  0%|          | 0/9912422 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Extracting ./data/MNIST/raw/train-images-idx3-ubyte.gz to ./data/MNIST/raw\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz\n",
      "Failed to download (trying next):\n",
      "HTTP Error 403: Forbidden\n",
      "\n",
      "Downloading https://ossci-datasets.s3.amazonaws.com/mnist/train-labels-idx1-ubyte.gz\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Downloading https://ossci-datasets.s3.amazonaws.com/mnist/train-labels-idx1-ubyte.gz to ./data/MNIST/raw/train-labels-idx1-ubyte.gz\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "6d31e8c56a294358821dfa2766d4f828",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "  0%|          | 0/28881 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Extracting ./data/MNIST/raw/train-labels-idx1-ubyte.gz to ./data/MNIST/raw\n",
      "\n",
      "Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz\n",
      "Failed to download (trying next):\n",
      "HTTP Error 403: Forbidden\n",
      "\n",
      "Downloading https://ossci-datasets.s3.amazonaws.com/mnist/t10k-images-idx3-ubyte.gz\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Downloading https://ossci-datasets.s3.amazonaws.com/mnist/t10k-images-idx3-ubyte.gz to ./data/MNIST/raw/t10k-images-idx3-ubyte.gz\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "273c4f79e2fc4f6b90cb2693326f6b87",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "  0%|          | 0/1648877 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Extracting ./data/MNIST/raw/t10k-images-idx3-ubyte.gz to ./data/MNIST/raw\n",
      "\n",
      "Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz\n",
      "Failed to download (trying next):\n",
      "HTTP Error 403: Forbidden\n",
      "\n",
      "Downloading https://ossci-datasets.s3.amazonaws.com/mnist/t10k-labels-idx1-ubyte.gz\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Downloading https://ossci-datasets.s3.amazonaws.com/mnist/t10k-labels-idx1-ubyte.gz to ./data/MNIST/raw/t10k-labels-idx1-ubyte.gz\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "aa1f2e4d99bb40faaa58fbb17645dffe",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "  0%|          | 0/4542 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Extracting ./data/MNIST/raw/t10k-labels-idx1-ubyte.gz to ./data/MNIST/raw\n",
      "\n"
     ]
    },
    {
     "data": {
      "image/png": "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rHg8jltvvRWNjY3QaDRwu9249NJL8corr/DnVNbY3nnnnZDL5fjHP/5R9l433XQT1Go19u/fPyPHOh243W7o9fpplQFOZnw73RhJ77N27Vq8/PLLuOiii6DX6/Gf//mfAIBsNos777wTy5Ytg0ajQV1dHT796U8jm82OOp5f/OIX2LRpE3Q6Hex2O66//vpZ3en/yle+glKphE9+8pMz8v4ymQw/+clPkEwm+fmme7tQKOCLX/wiH18bGxvxn//5n6POU+V9TzQ2NpaVrfz0pz+FTCbDP//5T3z0ox+F2+1GbW3tjPxe4/H0008jGAziox/9aNnjN998M5LJJP7yl79M22eNJxWVyWTo7u7mz5vM/HS6c/fggw9izZo10Gg0qKmpwc033zxn8txt27aNWn8tX74ca9aswdGjR6ftc+666y586lOfAgA0NTWVndurrroKGzduLHv+FVdcAZlMVnZuX3zxRchkMjzxxBP8sc7OTlxzzTWw2+3Q6/U477zzpvW6mCkeeeQRNDU1Ydu2bdP6vnv37sVll10Gp9MJnU6HpqYmfOADHyh7TqlUwje/+U2sWbMGWq0WHo8HH/7whxEOh/lz3vrWt6K5uXnMzzj//PNx7rnnlj02mbGXxvkjR47gda97HfR6PbxeL77yla9M028/PseOHcMTTzyBT33qU3A4HMhkMsjn8zPyWfl8HnfffTeWL18OrVYLh8OBCy+8EP/3f//Hn1NZY/uTn/wEMpkMP/7xj8ve695774VMJsNf//rXGTnWBQebRzzxxBMMALvyyivZq6++ynp7e9mvf/1rZjab2a233jqtn3Xo0CFmsVjY6tWr2X333cceeOABdtFFFzGZTMZ+//vf8+c9/fTTDAB7+umn+WMPPvggA8C2b9/Ovv3tb7NPfOITzG63s5aWFrZjx45Rr92wYQPbtGkT+8Y3vsHuuusuptfr2ZYtW/jznnzySbZ+/XrmdDrZww8/zB5++GH2+OOPT+vvOxEGg4G9733vm5H3TqVSbNWqVUylUrGPf/zj7Nvf/jbbvn07A8C++c1v8ud1dXXxc7Vs2TJ23333sa985SvM6XSy2tpalsvl+HMn+93NB1588UUml8vZrl27+O94//33T9v7JxIJ9vDDD7PW1lZWW1vLr5+hoSFWKpXYJZdcwmQyGbvxxhvZAw88wK644goGoOx+ouP6yU9+Mur9AbA777yT//vOO+9kANjq1avZ2972Nvbggw+y7373u9P2+0yG2267jQFg//jHP9imTZsYAKZWq9l1113HgsHgtH7Wu971LqZWq9knPvEJ9sMf/pDdd9997IorrmC/+MUv+HPe9773sYaGBv7vXC7HNmzYwBoaGlgsFmOMMfa3v/2NAWBf/OIXp/X4poNwOMz8fj87cOAA+8AHPsAAsB/84AfT9v4TjW+THSMZY2zHjh2sqqqKuVwu9u///u/s+9//PvvDH/7AisUie+Mb38j0ej279dZb2fe//312yy23MKVSyd72treVvceXvvQlJpPJ2HXXXccefPBBdvfddzOn08kaGxtZOByett95PHp6ephOp2O/+tWvGGOn7q+bb755Wj/j4YcfZtu3b2cajYaf746ODsbYqWsVALv66qvZd7/7Xfbe976XAWBvf/vby96j8r4nGhoayuaKn/zkJ3w82LFjB/vOd77DvvzlL0/r73M6vvSlLzEAbHh4uOzxbDbL5HI5+8QnPjFtn0XnU/rT0NDAdDodGxkZYYxNfn6a6NzROPuGN7yBfec732G33HILUygUbPPmzWVz4VxSKpWY1+tlb3zjG6ftPffv38/e+c53MgDsG9/4Bj/HiUSCff3rX2dyuZxFo1H++TabjcnlcvbJT36Sv8f9999f9ryhoSHm8XiYyWRid9xxB/v617/O2tramFwun3frBSmvvPIKA8DuuOOOaX3f4eFhZrPZ2IoVK9j999/PHnroIXbHHXewVatWlT3vxhtvZEqlkn3oQx9i3/ve99hnPvMZZjAYyq7Bn//85wwAe+mll8pe293dPWqtM9mxd8eOHaympobV1dWx//iP/2APPvggu+SSSxgA9te//nVaz0Ul3/nOdxgA9thjj/HPVCgU7PLLL2ddXV3T+ln/+Z//yWQyGfvQhz7EHnroIfa1r32NvfOd7ywbP2kckPLWt76VWSwW1tvbyxhj7MCBA0ytVrMPfvCD03p8C5l5FdgyxtgXv/hFptPpGAD+M903NmOMvf71r2fr1q1jmUyGP1Yqldi2bdvY8uXL+WOVgW02m2UOh4Nt3ryZ5fN5/ryf/vSnDMCYge2qVatYNpvlj3/rW99iANjBgwf5Y295y1vKFsezyUwGtt/85jcZgLJAIJfLsfPPP58ZjUa+8KfgyuFwsFAoxJ/7xz/+kQFgf/7zn/ljk/3u5ppSqcS2bNnC3vnOdzLG2IwEtsSOHTvYmjVryh77wx/+wACwL33pS2WPX3311Uwmk7GTJ0+WHddUAlv6neaCK6+8kl8r7373u9nvfvc79vnPf54plUq2bds2ViqVpu2zLBbLaQOPysCWMcYOHjzI1Go1u/HGG1k4HGZer5ede+65ZWPGfGHlypV8rDUajexzn/scKxaL0/oZ441vUxkjd+zYwQCw733ve2Xv8fDDDzO5XM527txZ9vj3vvc9BoA9//zzjLFTiy2FQsHuueeesucdPHiQKZXKUY/PBFdffTXbtm0b//dMBLaMnbomDQZD2WP79u1jANiNN95Y9vgnP/lJBoA99dRTZcc1lcD2wgsvZIVCYVp/h8ly8803M4VCMeb/uVwudv3118/YZ3/lK19hANjPf/5z/thk56fxzp3f72dqtZq98Y1vLLsPH3jgAQaA/fjHP56x32cqPPzwwwwA+9GPfjSt73v//fczAKMCiT179pQFNwcOHGAA2DXXXMO2bt3Kn3fllVeyDRs28H/feuutDEDZ+BCPx1lTUxNrbGyc9rFuuqAE7pEjR6b1fR9//HEGgO3Zs2fc5+zcuZMBYL/85S/LHqcELT0ejUaZRqNht912W9nzvvKVrzCZTMZ6enoYY1Mbe2mcl95T2WyWVVVVsXe84x1n9ktPko997GN8bXH55Zez3/zmN+z+++9nRqORtbS0sGQyOW2f1dbWxt7ylrdM+JyxAtvBwUFmt9vZpZdeyrLZLNuwYQOrr6/niRwBY/NKigyckjpddNFF+MEPfoDHHnsMH/jAB3DvvffigQcemLbPCIVCeOqpp3DttdciHo8jEAggEAggGAzisssuQ3t7OwYGBsZ87d69exEMBvGhD32oTPv+7ne/e9w6nve///1lMp7t27cDOCWPWez89a9/RVVVFd75znfyx1QqFT72sY8hkUjgn//8Z9nzr7vuurLzWHmuzua7m21++tOf4uDBg7jvvvvm5PP/+te/QqFQ4GMf+1jZ47fddhsYY2VSranykY985GwP74whg43NmzfjF7/4Bd7xjnfgC1/4Ar74xS9i165doyTAZ4PVasWLL74In883pdetXbsWd999N374wx/isssuQyAQwM9+9rN5WS/zk5/8BH/729/w4IMPYtWqVUin0zMm7R6PyY6RGo0G73//+8se++1vf4tVq1ahtbWVjweBQACXXHIJgFNSVeCUYVOpVMK1115b9ryqqiosX76cP2+mePrpp/HYY4/NmTs0ydQ+8YlPlD1+2223AcBZSTM/9KEPQaFQnPnBnQUTmTtqtVqk0+kZ+dynn34at99+O/793/8d73nPewCc2fxUee7+/ve/I5fL4dZbb4VcLi97ntlsnhcS2mPHjuHmm2/G+eefj/e9732z8pkbNmyA0WjEs88+CwDYuXMnamtr8d73vhevvPIKUqkUGGN47rnn+PgBnLrut2zZUlYyYzQacdNNN6G7uxtHjhyZleOfCqVSCb/+9a+xYcMGrFq1alrfm7w4/ud//mdcme1vf/tbWCwWXHrppWVj5aZNm2A0GvlYaTab8aY3vQmPPvpoWXnTb37zG5x33nmor68HMPWx12g0lpVuqdVqbNmyZcbXzLS2qKqqwl/+8hdce+21+OQnP4mHHnoIHR0deOSRR6bts6xWKw4fPoz29vYpva6qqgrf/e538X//93/Yvn079u3bhx//+Mcwm83TdmwLnXm1yvr1r3+Nm266CSdOnOC1JldddRVKpRI+85nP4J3vfOe4xdyJRKLMUU6hUMDlco353JMnT4Ixhs9//vP4/Oc/P+Zz/H4/vF7vqMd7enoAAMuWLSt7XKlUjlsXSzc3QYGbtFZhoVAsFjEyMlL2mN1uH3dh0dPTg+XLl5dN0AD4YE3nkzjduTqb7242icViuP322/GpT30KdXV1U379yMhIWXBhNBqnbPLV09ODmpoamEymssfHO/dToamp6Yxfe7aQWZQ0WQIA73rXu3D77bdj165deMMb3jDma9PpNKLRaNljVVVV437WV77yFbzvfe9DXV0dNm3ahDe/+c1473vfO25dkZRPfepT+PWvf42XXnoJ9957L1avXn3a18wF559/Pv/79ddfz6+PidpSRaPRsoBBrVbDbref8TFMdoz0er2jxpr29nYcPXp03PHe7/fz5zHGxm2dMZMmYIVCAR/72Mfwnve8B5s3b57y60OhEHK5HP+3TqeDxWKZ0nv09PRALpePmruqqqpgtVoX9HggPTdSMpnMhOZyuVwOoVCo7DGXy3XaIL2/vx/XXXcdLrjgAnz961/nj5/J/FR57uh7WLlyZdnjarUazc3NZ/U9TQdDQ0N4y1veAovFgt/97nenPVdTHXPHQ6FQ4Pzzz8fOnTsBnApst2/fjgsvvBDFYhG7d++Gx+NBKBQqC2x7enqwdevWUe8nnQen09V5OvjnP/+JgYGBSbftm8r4sGPHDrzjHe/A3XffjW984xu4+OKL8fa3vx3vete7oNFoAJwaK6PRKNxu95jvQWMqcGoz4g9/+ANeeOEFbNu2DR0dHXj55ZfLEnhTHXtra2tHecDYbDYcOHBg/JMwDdBYce2115atWa+55hq85z3vwa5du3DjjTeO+dqpjiVf+MIX8La3vQ0rVqzA2rVrcfnll+M973kPzjnnnNMe5/XXX49f/OIX+Mtf/oKbbroJr3/96yf7Ky4J5lVg++CDD2LDhg2jzCeuvPJK/PSnP8Wrr7467oL1q1/9Ku6++27+74aGhjIzBynUmuOTn/wkLrvssjGfUzn5nw3jXdjSDNdCoa+vb9RE/PTTT49pmnUmnO5czfZ3d6Z89atfRS6Xw3XXXcevw/7+fgCnFuvd3d2oqakZNyGwefPmsgXMnXfeOaaZy3QwnonYRLt2c+lEXFNTA2C0yRxNwhMljH7zm9+M2vGb6D689tprsX37djz++ON48skncf/99+O+++7D73//e7zpTW+a8Dg7Ozt5NvbgwYMTPne+YLPZcMkll+CXv/zlhIHtf/zHf5QZwO3YsWOUwd5UmOwYOdZ1VyqVsG7durIAQwollkqlEjeVGevzZtId/uc//zmOHz+O73//+6PmpXg8ju7ubm7cNRZXXXVVmbrlfe9735hmb5PhbEwDxxsT5nI8qK6uRrFYhN/vL1uI53I5BINBPl6Mxa5du/C6172u7LGurq4JzRtzuRyuvvpqaDQaPProo2UqjDOZn+ba1X0qRKNRvOlNb0IkEsHOnTsnPLfEVMfcibjwwgtxzz33IJPJYOfOnbjjjjtgtVqxdu1a7Ny5k88J0sB2IfLLX/4Scrl8VPJ2PKYyPshkMvzud7/D7t278ec//xn/+7//iw984AP42te+ht27d8NoNKJUKsHtduOXv/zlmO8hTSJeccUV0Ov1ePTRR7Ft2zY8+uijkMvluOaaa/hzpjr2ztWaeby1hUKhgMPhmHBtMdWx5KKLLkJHRwf++Mc/4sknn8QPf/hDfOMb38D3vve9cYNnIhgMYu/evQCAI0eOoFQqjdo8WsrMq8B2eHh4TDkvySUKhcK4r33ve99bJjWZaLKg3RaVSjVuoDweDQ0NAE5lZqUXcaFQQHd396SyLWMxWw7FZ0tVVVWZaxuACftdNjQ04MCBA6NuvGPHjvH/nwpn893NJr29vQiHw7xtlZR7770X9957L1599VWsX79+zNf/8pe/LNsRm8wOYSUNDQ34+9//jng8XrZrW3nu6Z6rdNyc652B8di0aRMeeuihUZI+kguPt3MHAJdddtmo6/d0VFdX46Mf/Sg++tGPwu/3Y+PGjbjnnnsmDGxLpRJuuOEGmM1m3Hrrrbj33ntx9dVX46qrrprSZ88FY+2wVPLpT3+6TCp2unYqMzm+tbS0YP/+/Xj9618/4ee0tLSAMYampiasWLFixo5nLHp7e5HP53l/RCk///nP8fOf/xyPP/74mC0/AOBrX/ta2aJqMgFFJQ0NDSiVSmhvby+TNw4PDyMSiZSNxTabbdR4kMvl5mXPcxpD9+7dize/+c388b1796JUKo07xgKn5q7K8eB0u4kf+9jHsG/fPjz77LOjFsDTMT/R93D8+PGycT+Xy6Grq2vO5r1MJoMrrrgCJ06cwN///vdJK1CmOuZOdA9v374duVwOv/rVrzAwMMAD2IsuuogHtitWrCj7XhoaGnD8+PFR73Wma5CZJpvN4rHHHsPFF1886fv8TMaH8847D+eddx7uuecePPLII3j3u9+NX//617jxxhvR0tKCv//977jgggtOm3gxGAx461vfit/+9rf4+te/jt/85jfYvn172THM5dg7FTZt2gQAo9YWuVwOgUBgwrXFmYwldrsd73//+/H+978fiUQCF110Ee66667TBrY333wz4vE4/uu//gu33347vvnNb44qMVnSzH5Z7/i89a1vZWq1mh0/frzs8be//e1MLpezgYGBafusiy++mNntdubz+Ub9n9/v53+fDvOo3/72t2XvP5ZZz3XXXcesVuv0/HJTZDbMox555BH+WD6fZxdccMGY5lFjGSuhwshkst/dXPLyyy+zxx9/vOzn+9//PgPAbrjhBvb444+zSCQybZ83kXnUvffeW/b4ddddV2YexRhjTqeT/cu//EvZ88i8YizzKHIAnQsGBweZRqNhF154YZnxx+233z6mQ+OZUigUxvyONm/ezM4991z+77HMo8gA5U9/+hMrFots27ZtzO12z+l5q6TSRZaxU/ehyWRi27dv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",
      "text/plain": [
       "<Figure size 1200x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "g = torch.Generator()\n",
    "g.manual_seed(SEED)\n",
    "np.random.seed(SEED)\n",
    "torch.manual_seed(SEED)\n",
    "random.seed(SEED)\n",
    "\n",
    "train_transform = transforms.Compose(\n",
    "    [  # Workaround: stride=1, padding_mode='replicate' is replaced by\n",
    "        transforms.Pad(1, padding_mode=\"edge\"),\n",
    "        transforms.ToTensor(),\n",
    "        transforms.Normalize((MEAN,), (STD,)),\n",
    "    ]\n",
    ")\n",
    "test_transform = transforms.Compose(\n",
    "    [  # Workaround: stride=1, padding_mode='replicate' is replaced by\n",
    "        transforms.Pad(1, padding_mode=\"edge\"),\n",
    "        transforms.ToTensor(),\n",
    "        transforms.Normalize((MEAN,), (STD,)),\n",
    "    ]\n",
    ")\n",
    "\n",
    "train_dataset = datasets.MNIST(download=True, root=\"./data\", train=True, transform=train_transform)\n",
    "\n",
    "test_dataset = datasets.MNIST(download=True, root=\"./data\", train=False, transform=test_transform)\n",
    "\n",
    "train_loader = torch.utils.data.DataLoader(\n",
    "    train_dataset,\n",
    "    batch_size=BATCH_SIZE,\n",
    "    shuffle=True,\n",
    "    drop_last=True,\n",
    "    generator=g,\n",
    ")\n",
    "\n",
    "test_loader = torch.utils.data.DataLoader(\n",
    "    test_dataset,\n",
    "    batch_size=BATCH_SIZE,\n",
    "    shuffle=True,\n",
    "    drop_last=True,\n",
    "    generator=g,\n",
    ")\n",
    "\n",
    "plot_dataset(train_loader)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Model architecture\n",
    "\n",
    "The following cell shows the architecture of the NN-20 model. It contains one convolutional layer and 19 linear ones. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Fp32MNIST(\n",
      "  (features_maps): Sequential(\n",
      "    (0): Conv2d(1, 1, kernel_size=(3, 3), stride=(1, 1))\n",
      "    (1): ReLU()\n",
      "    (2): BatchNorm2d(1, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "  )\n",
      "  (linears): Sequential(\n",
      "    (0): Linear(in_features=784, out_features=92, bias=True)\n",
      "    (1): ReLU()\n",
      "    (2): BatchNorm1d(92, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "    (3): Linear(in_features=92, out_features=92, bias=True)\n",
      "    (4): ReLU()\n",
      "    (5): BatchNorm1d(92, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "    (6): Linear(in_features=92, out_features=92, bias=True)\n",
      "    (7): ReLU()\n",
      "    (8): BatchNorm1d(92, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "    (9): Linear(in_features=92, out_features=92, bias=True)\n",
      "    (10): ReLU()\n",
      "    (11): BatchNorm1d(92, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "    (12): Linear(in_features=92, out_features=92, bias=True)\n",
      "    (13): ReLU()\n",
      "    (14): BatchNorm1d(92, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "    (15): Linear(in_features=92, out_features=92, bias=True)\n",
      "    (16): ReLU()\n",
      "    (17): BatchNorm1d(92, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "    (18): Linear(in_features=92, out_features=92, bias=True)\n",
      "    (19): ReLU()\n",
      "    (20): BatchNorm1d(92, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "    (21): Linear(in_features=92, out_features=92, bias=True)\n",
      "    (22): ReLU()\n",
      "    (23): BatchNorm1d(92, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "    (24): Linear(in_features=92, out_features=92, bias=True)\n",
      "    (25): ReLU()\n",
      "    (26): BatchNorm1d(92, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "    (27): Linear(in_features=92, out_features=92, bias=True)\n",
      "    (28): ReLU()\n",
      "    (29): BatchNorm1d(92, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "    (30): Linear(in_features=92, out_features=92, bias=True)\n",
      "    (31): ReLU()\n",
      "    (32): BatchNorm1d(92, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "    (33): Linear(in_features=92, out_features=92, bias=True)\n",
      "    (34): ReLU()\n",
      "    (35): BatchNorm1d(92, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "    (36): Linear(in_features=92, out_features=92, bias=True)\n",
      "    (37): ReLU()\n",
      "    (38): BatchNorm1d(92, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "    (39): Linear(in_features=92, out_features=92, bias=True)\n",
      "    (40): ReLU()\n",
      "    (41): BatchNorm1d(92, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "    (42): Linear(in_features=92, out_features=92, bias=True)\n",
      "    (43): ReLU()\n",
      "    (44): BatchNorm1d(92, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "    (45): Linear(in_features=92, out_features=92, bias=True)\n",
      "    (46): ReLU()\n",
      "    (47): BatchNorm1d(92, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "    (48): Linear(in_features=92, out_features=92, bias=True)\n",
      "    (49): ReLU()\n",
      "    (50): BatchNorm1d(92, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "    (51): Linear(in_features=92, out_features=92, bias=True)\n",
      "    (52): ReLU()\n",
      "    (53): BatchNorm1d(92, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "    (54): Linear(in_features=92, out_features=10, bias=True)\n",
      "  )\n",
      ")\n"
     ]
    }
   ],
   "source": [
    "fp32_mnist = Fp32MNIST(nb_layers=20).to(DEVICE)\n",
    "print(fp32_mnist)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Benchmark the models\n",
    "\n",
    "In the next sections of this notebook, we apply the same experimental protocol to both NN-20 and NN-50 models.\n",
    "\n",
    "To make a custom neural network FHE-compatible, it's necessary to quantize both the network and its inputs. Post-Training Quantization (PTQ) involves quantizes a pre-trained floating-point model directly, and does not require re-training. In Concrete ML, the compilation of the custom NNs is performed through `compile_torch_model` function\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In the compilation step, the compiler requires a representative set of data,  named `data_calibration` below, to evaluate the maximum integer bit-width within the graph."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Running NN-20 on a 128-core machine machine:Accuracy in fp32 : 98.067% for the test set\n",
      "Accuracy with FHE-simulation mode : 95.803% for the test set\n",
      "FHE Latency on encrypted data : 1.918s per encrypted sample.\n",
      "Number of PBS: 2440\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Running NN-50 on a 128-core machine machine:Accuracy in fp32 : 97.446% for the test set\n",
      "Accuracy with FHE-simulation mode : 94.631% for the test set\n",
      "FHE Latency on encrypted data : 4.902s per encrypted sample.\n",
      "Number of PBS: 5200\n"
     ]
    }
   ],
   "source": [
    "data_calibration = next(iter(train_loader))[0]\n",
    "\n",
    "results_cml = {}\n",
    "\n",
    "for nb_layers in [20, 50]:\n",
    "\n",
    "    fp32_mnist = Fp32MNIST(nb_layers=nb_layers).to(DEVICE)\n",
    "\n",
    "    checkpoint = torch.load(\n",
    "        f\"./checkpoints/MNIST/MLP_{nb_layers}/fp32/MNIST_fp32_state_dict.pt\", map_location=DEVICE\n",
    "    )\n",
    "    fp32_mnist.load_state_dict(checkpoint)\n",
    "    fp32_mnist.eval()\n",
    "\n",
    "    acc_test = torch_inference(fp32_mnist, test_loader, device=DEVICE)\n",
    "\n",
    "    # The model is compiled through 'compile_torch_model' method\n",
    "    # We use approximate rounding and a p_error value of 0.1\n",
    "    # These values work well for neural networks which are robust to noise\n",
    "    # in the computation of intermediate values.\n",
    "    q_module = compile_torch_model(\n",
    "        fp32_mnist.to(DEVICE),\n",
    "        torch_inputset=data_calibration,\n",
    "        n_bits=6,\n",
    "        rounding_threshold_bits={\"n_bits\": 6, \"method\": \"APPROXIMATE\"},\n",
    "        p_error=0.1,\n",
    "    )\n",
    "\n",
    "    fhe_timing = []\n",
    "    y_predictions = []\n",
    "    fhe_samples = 3\n",
    "\n",
    "    # The model is evaluated through all the test data-set in 'simulation' mode\n",
    "    for i, (data, labels) in enumerate(test_loader):\n",
    "\n",
    "        data, labels = data.detach().cpu().numpy(), labels.detach().cpu().numpy()\n",
    "        simulate_predictions = q_module.forward(data, fhe=\"simulate\")\n",
    "        y_predictions.extend(simulate_predictions.argmax(1) == labels)\n",
    "\n",
    "        # Measure FHE latency on three samples and take the minimum\n",
    "        if i <= fhe_samples:\n",
    "            start_time = time.time()\n",
    "            q_module.forward(data[0, None], fhe=\"execute\")\n",
    "            fhe_timing.append((time.time() - start_time))\n",
    "\n",
    "    results_cml[nb_layers] = [acc_test, np.mean(y_predictions), np.min(fhe_timing)]\n",
    "\n",
    "    print(\n",
    "        f\"Running NN-{nb_layers} on a {MACHINE} machine:\"\n",
    "        f\"Accuracy in fp32 : {results_cml[nb_layers][0]:.3%} for the test set\\n\"\n",
    "        f\"Accuracy with FHE-simulation mode : {results_cml[nb_layers][1]:.3%} for the test set\\n\"\n",
    "        f\"FHE Latency on encrypted data : {results_cml[nb_layers][2]:.3f}s per encrypted sample.\\n\"\n",
    "        f\"Number of PBS: {q_module.fhe_circuit.statistics['programmable_bootstrap_count']}\"\n",
    "    )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Conclusion\n",
    "\n",
    "Here is a recap of the results obtained by running this notebook on a `m6i.metal` instance, compared to the results in the whitepaper [1]."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style type=\"text/css\">\n",
       "</style>\n",
       "<table id=\"T_4ba03\">\n",
       "  <thead>\n",
       "    <tr>\n",
       "      <th class=\"blank level0\" >&nbsp;</th>\n",
       "      <th id=\"T_4ba03_level0_col0\" class=\"col_heading level0 col0\" >Num Layers</th>\n",
       "      <th id=\"T_4ba03_level0_col1\" class=\"col_heading level0 col1\" >Accuracy [1]</th>\n",
       "      <th id=\"T_4ba03_level0_col2\" class=\"col_heading level0 col2\" >FHE Latency [1]</th>\n",
       "      <th id=\"T_4ba03_level0_col3\" class=\"col_heading level0 col3\" >Our m6i.metal Accuracy fp32</th>\n",
       "      <th id=\"T_4ba03_level0_col4\" class=\"col_heading level0 col4\" >Our m6i.metal Accuracy FHE</th>\n",
       "      <th id=\"T_4ba03_level0_col5\" class=\"col_heading level0 col5\" >Our m6i.metal FHE Latency</th>\n",
       "      <th id=\"T_4ba03_level0_col6\" class=\"col_heading level0 col6\" >Speedup</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th id=\"T_4ba03_level0_row0\" class=\"row_heading level0 row0\" >0</th>\n",
       "      <td id=\"T_4ba03_row0_col0\" class=\"data row0 col0\" >20</td>\n",
       "      <td id=\"T_4ba03_row0_col1\" class=\"data row0 col1\" >97.1%</td>\n",
       "      <td id=\"T_4ba03_row0_col2\" class=\"data row0 col2\" >21.17s</td>\n",
       "      <td id=\"T_4ba03_row0_col3\" class=\"data row0 col3\" >98.1%</td>\n",
       "      <td id=\"T_4ba03_row0_col4\" class=\"data row0 col4\" >95.8%</td>\n",
       "      <td id=\"T_4ba03_row0_col5\" class=\"data row0 col5\" >1.92s</td>\n",
       "      <td id=\"T_4ba03_row0_col6\" class=\"data row0 col6\" >11.0x</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_4ba03_level0_row1\" class=\"row_heading level0 row1\" >1</th>\n",
       "      <td id=\"T_4ba03_row1_col0\" class=\"data row1 col0\" >50</td>\n",
       "      <td id=\"T_4ba03_row1_col1\" class=\"data row1 col1\" >94.7%</td>\n",
       "      <td id=\"T_4ba03_row1_col2\" class=\"data row1 col2\" >43.91s</td>\n",
       "      <td id=\"T_4ba03_row1_col3\" class=\"data row1 col3\" >97.4%</td>\n",
       "      <td id=\"T_4ba03_row1_col4\" class=\"data row1 col4\" >94.6%</td>\n",
       "      <td id=\"T_4ba03_row1_col5\" class=\"data row1 col5\" >4.90s</td>\n",
       "      <td id=\"T_4ba03_row1_col6\" class=\"data row1 col6\" >9.0x</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n"
      ],
      "text/plain": [
       "<pandas.io.formats.style.Styler at 0x7f99e89357c0>"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df, fmt = format_results_df(PAPER_NOTES, results_cml, \"Our m6i.metal\")\n",
    "df.style.format(fmt)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A pre-computed comparison to a `hpc7a.96xlarge` instance with 192 vCPU is also shown when running this notebook on a large server machine."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style type=\"text/css\">\n",
       "</style>\n",
       "<table id=\"T_e8864\">\n",
       "  <thead>\n",
       "    <tr>\n",
       "      <th class=\"blank level0\" >&nbsp;</th>\n",
       "      <th id=\"T_e8864_level0_col0\" class=\"col_heading level0 col0\" >Num Layers</th>\n",
       "      <th id=\"T_e8864_level0_col1\" class=\"col_heading level0 col1\" >Accuracy [1]</th>\n",
       "      <th id=\"T_e8864_level0_col2\" class=\"col_heading level0 col2\" >FHE Latency [1]</th>\n",
       "      <th id=\"T_e8864_level0_col3\" class=\"col_heading level0 col3\" >Our hpc7a.96xlarge Accuracy fp32</th>\n",
       "      <th id=\"T_e8864_level0_col4\" class=\"col_heading level0 col4\" >Our hpc7a.96xlarge Accuracy FHE</th>\n",
       "      <th id=\"T_e8864_level0_col5\" class=\"col_heading level0 col5\" >Our hpc7a.96xlarge FHE Latency</th>\n",
       "      <th id=\"T_e8864_level0_col6\" class=\"col_heading level0 col6\" >Speedup</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th id=\"T_e8864_level0_row0\" class=\"row_heading level0 row0\" >0</th>\n",
       "      <td id=\"T_e8864_row0_col0\" class=\"data row0 col0\" >20</td>\n",
       "      <td id=\"T_e8864_row0_col1\" class=\"data row0 col1\" >97.1%</td>\n",
       "      <td id=\"T_e8864_row0_col2\" class=\"data row0 col2\" >21.17s</td>\n",
       "      <td id=\"T_e8864_row0_col3\" class=\"data row0 col3\" >98.7%</td>\n",
       "      <td id=\"T_e8864_row0_col4\" class=\"data row0 col4\" >95.9%</td>\n",
       "      <td id=\"T_e8864_row0_col5\" class=\"data row0 col5\" >0.99s</td>\n",
       "      <td id=\"T_e8864_row0_col6\" class=\"data row0 col6\" >21.3x</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_e8864_level0_row1\" class=\"row_heading level0 row1\" >1</th>\n",
       "      <td id=\"T_e8864_row1_col0\" class=\"data row1 col0\" >50</td>\n",
       "      <td id=\"T_e8864_row1_col1\" class=\"data row1 col1\" >94.7%</td>\n",
       "      <td id=\"T_e8864_row1_col2\" class=\"data row1 col2\" >43.91s</td>\n",
       "      <td id=\"T_e8864_row1_col3\" class=\"data row1 col3\" >97.5%</td>\n",
       "      <td id=\"T_e8864_row1_col4\" class=\"data row1 col4\" >94.8%</td>\n",
       "      <td id=\"T_e8864_row1_col5\" class=\"data row1 col5\" >3.03s</td>\n",
       "      <td id=\"T_e8864_row1_col6\" class=\"data row1 col6\" >14.5x</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n"
      ],
      "text/plain": [
       "<pandas.io.formats.style.Styler at 0x7f984811f550>"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# This benchmark was done on a hpc7 machine with 192-cores\n",
    "BENCH_HPC7A = {20: [0.987, 0.959, 0.995], 50: [0.9745, 0.9477, 3.03]}\n",
    "\n",
    "df, fmt = format_results_df(PAPER_NOTES, BENCH_HPC7A, \"Our hpc7a.96xlarge\")\n",
    "df.style.format(fmt)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As shown in the table above, on the `hpc7` instance, Concrete ML achieves a ~20x speed-up compared to the whitepaper."
   ]
  }
 ],
 "metadata": {
  "execution": {
   "timeout": 10800
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
